package com.example.utils;

/**
 * 地理工具类
 *
 * @author: Fred
 * @email 453086@qq.com
 * @create: 2024-12-25 08:41
 */
public class GeoUtils {

    private static final int METERS_PER_LATITUDE = 111120;
    // 地球半径（单位：米）
    private static final int EARTH_RADIUS = 6371000;

    /**
     * 计算两个经纬度点之间的航向（方位角）。
     *
     * @return 航向角度，范围 [0, 360)
     */
    public static double calculateBearing(double lat1, double lon1, double lat2, double lon2) {
        // 转换经纬度为弧度
        double phi1 = Math.toRadians(lat1);
        double phi2 = Math.toRadians(lat2);
        double lambda1 = Math.toRadians(lon1);
        double lambda2 = Math.toRadians(lon2);

        // 经度差
        double deltaLambda = lambda2 - lambda1;

        // 航向计算公式
        double y = Math.sin(deltaLambda) * Math.cos(phi2);
        double x = Math.cos(phi1) * Math.sin(phi2) - Math.sin(phi1) * Math.cos(phi2) * Math.cos(deltaLambda);
        double theta = Math.atan2(y, x);

        // 转换为角度并规范到 [0, 360)
        return (Math.toDegrees(theta) + 360) % 360;
    }

    /**
     * 计算两个经纬度点之间的近似距离
     *
     * @return 距离
     */
    public static double calculateApproxDistance(double lat1, double lon1, double lat2, double lon2) {

        // 每经度1度对应的距离（随纬度变化）
        double metersPerDegreeLon = METERS_PER_LATITUDE * Math.cos(Math.toRadians((lat1 + lat2) / 2));

        // 纬度和经度的差值（单位：度）
        double deltaLat = lat2 - lat1;
        double deltaLon = lon2 - lon1;

        // 距离近似公式
        return Math.sqrt(Math.pow(deltaLat * METERS_PER_LATITUDE, 2) + Math.pow(deltaLon * metersPerDegreeLon, 2));
    }

    /**
     * 计算两个经纬度点之间的大圆距离
     *
     * @return 距离
     */
    public static double calculateHaversineDistance(double lat1, double lon1, double lat2, double lon2) {

        double phi1 = Math.toRadians(lat1);
        double phi2 = Math.toRadians(lat2);
        double deltaPhi = Math.toRadians(lat2 - lat1);
        double deltaLambda = Math.toRadians(lon2 - lon1);

        double a = Math.sin(deltaPhi / 2) * Math.sin(deltaPhi / 2) +
                Math.cos(phi1) * Math.cos(phi2) *
                        Math.sin(deltaLambda / 2) * Math.sin(deltaLambda / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        return EARTH_RADIUS * c;
    }
}
